<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>http缓存 | yanyan</title>
    <meta name="description" content="Yan&#39;s blog">
    <meta name="generator" content="VuePress 1.4.0">
    <script src="https://cdn.bootcss.com/jquery/3.5.0/jquery.slim.min.js"></script>
  <script src="https://cdn.bootcss.com/fancybox/3.5.7/jquery.fancybox.min.js"></script>
  <link rel="stylesheet" type="text/css" href="https://cdn.bootcss.com/fancybox/3.5.7/jquery.fancybox.min.css">
  <link rel="shortcut icon" type="image/x-icon" href="./favicon.ico">
    
    <link rel="preload" href="/assets/css/0.styles.e1b3f17d.css" as="style"><link rel="preload" href="/assets/js/app.e358a08d.js" as="script"><link rel="preload" href="/assets/js/2.88fa18d1.js" as="script"><link rel="preload" href="/assets/js/35.fadf5d03.js" as="script"><link rel="prefetch" href="/assets/js/10.23baf844.js"><link rel="prefetch" href="/assets/js/11.45c148ba.js"><link rel="prefetch" href="/assets/js/12.e5930132.js"><link rel="prefetch" href="/assets/js/13.0547cd14.js"><link rel="prefetch" href="/assets/js/14.3e67795b.js"><link rel="prefetch" href="/assets/js/15.51129890.js"><link rel="prefetch" href="/assets/js/16.6987f89d.js"><link rel="prefetch" href="/assets/js/17.2807cff5.js"><link rel="prefetch" href="/assets/js/18.855e1707.js"><link rel="prefetch" href="/assets/js/19.6da24791.js"><link rel="prefetch" href="/assets/js/20.e24d4aef.js"><link rel="prefetch" href="/assets/js/21.6efc6fba.js"><link rel="prefetch" href="/assets/js/22.10447f0f.js"><link rel="prefetch" href="/assets/js/23.9154cc24.js"><link rel="prefetch" href="/assets/js/24.9ad529fc.js"><link rel="prefetch" href="/assets/js/25.4c092e0a.js"><link rel="prefetch" href="/assets/js/26.debdaa01.js"><link rel="prefetch" href="/assets/js/27.8b90b660.js"><link rel="prefetch" href="/assets/js/28.1a323e01.js"><link rel="prefetch" href="/assets/js/29.6f108fc9.js"><link rel="prefetch" href="/assets/js/3.7210d3aa.js"><link rel="prefetch" href="/assets/js/30.e7df1937.js"><link rel="prefetch" href="/assets/js/31.2cb3120f.js"><link rel="prefetch" href="/assets/js/32.eb64932c.js"><link rel="prefetch" href="/assets/js/33.cac3e2f0.js"><link rel="prefetch" href="/assets/js/34.19ea35c4.js"><link rel="prefetch" href="/assets/js/36.88b681f1.js"><link rel="prefetch" href="/assets/js/37.2a799db9.js"><link rel="prefetch" href="/assets/js/38.2741a2bf.js"><link rel="prefetch" href="/assets/js/39.359ceb72.js"><link rel="prefetch" href="/assets/js/4.9e938666.js"><link rel="prefetch" href="/assets/js/40.56fd4a10.js"><link rel="prefetch" href="/assets/js/41.e72117ad.js"><link rel="prefetch" href="/assets/js/42.63a6e190.js"><link rel="prefetch" href="/assets/js/43.c8072421.js"><link rel="prefetch" href="/assets/js/44.84cd8367.js"><link rel="prefetch" href="/assets/js/45.0ac810b0.js"><link rel="prefetch" href="/assets/js/46.bb83ff34.js"><link rel="prefetch" href="/assets/js/47.a9333a81.js"><link rel="prefetch" href="/assets/js/48.526b5494.js"><link rel="prefetch" href="/assets/js/49.73b61cc6.js"><link rel="prefetch" href="/assets/js/5.88b252c7.js"><link rel="prefetch" href="/assets/js/50.f34ab799.js"><link rel="prefetch" href="/assets/js/51.d06a49d9.js"><link rel="prefetch" href="/assets/js/52.348d5482.js"><link rel="prefetch" href="/assets/js/6.0face56b.js"><link rel="prefetch" href="/assets/js/7.31eca58d.js"><link rel="prefetch" href="/assets/js/8.69e9ce95.js"><link rel="prefetch" href="/assets/js/9.f25df9e1.js">
    <link rel="stylesheet" href="/assets/css/0.styles.e1b3f17d.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/" class="home-link router-link-active"><!----> <span class="site-name">yanyan</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <!----></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><!---->  <ul class="sidebar-links"><li><section class="sidebar-group collapsable depth-0"><a href="/html/001" class="sidebar-heading clickable"><span>HTML</span> <span class="arrow right"></span></a> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/css/image" class="sidebar-heading clickable"><span>CSS</span> <span class="arrow right"></span></a> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/js/001" class="sidebar-heading clickable"><span>JS-基础</span> <span class="arrow right"></span></a> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/js-v8/001" class="sidebar-heading clickable"><span>JS-V8引擎原理 </span> <span class="arrow right"></span></a> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/js-async/001" class="sidebar-heading clickable"><span>JS-异步I/O及异步编程</span> <span class="arrow right"></span></a> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/http/000" class="sidebar-heading clickable open"><span>HTTP</span> <span class="arrow down"></span></a> <ul class="sidebar-links sidebar-group-items"><li><a href="/http/001.html" class="sidebar-link">01 HTTP 的特点？HTTP 的缺点</a></li><li><a href="/http/002.html" class="sidebar-link">02 说一说从输入URL到页面呈现发生了什么？——网络篇</a></li><li><a href="/http/003.html" class="sidebar-link">03 说一说从输入URL到页面呈现发生了什么？——解析算法篇</a></li><li><a href="/http/004.html" class="sidebar-link">04 说一说从输入URL到页面呈现发生了什么？——渲染过程篇</a></li><li><a href="/http/005.html" class="sidebar-link">05 谈谈你对重绘和回流的理解</a></li><li><a href="/http/006.html" class="sidebar-link">06 XSS攻击</a></li><li><a href="/http/007.html" class="sidebar-link">07 CSRF攻击</a></li><li><a href="/http/008.html" class="sidebar-link">08 (传统RSA版本)HTTPS为什么让数据传输更安全</a></li><li><a href="/http/009.html" class="sidebar-link">09 Https TLS 1.2  1.3</a></li><li><a href="/http/010.html" class="sidebar-link">10 Cookie</a></li><li><a href="/http/011.html" class="sidebar-link">11 什么是跨域？浏览器如何拦截响应？如何解决</a></li><li><a href="/http/012.html" class="sidebar-link">12 Http2</a></li><li><a href="/http/013.html" class="active sidebar-link">13 Http缓存</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/http/013.html#强缓存" class="sidebar-link">强缓存</a></li><li class="sidebar-sub-header"><a href="/http/013.html#协商缓存" class="sidebar-link">协商缓存</a></li><li class="sidebar-sub-header"><a href="/http/013.html#两者对比" class="sidebar-link">两者对比</a></li><li class="sidebar-sub-header"><a href="/http/013.html#缓存位置" class="sidebar-link">缓存位置</a></li><li class="sidebar-sub-header"><a href="/http/013.html#总结" class="sidebar-link">总结</a></li></ul></li></ul></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/extend/002" class="sidebar-heading clickable"><span>拓展阅读</span> <span class="arrow right"></span></a> <!----></section></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="http缓存"><a href="#http缓存" class="header-anchor">#</a> http缓存</h1> <p><strong>概览</strong></p> <p><a data-fancybox="" title="result" href="/images/html-015.jpg"><img src="/images/html-015.jpg" alt="result"></a></p> <p>缓存是性能优化中非常重要的一环，浏览器的缓存机制对开发也是非常重要的知识点。接下来以三个部分来把浏览器的缓存机制说清楚：</p> <ul><li>强缓存</li> <li>协商缓存</li> <li>缓存位置</li></ul> <h2 id="强缓存"><a href="#强缓存" class="header-anchor">#</a> 强缓存</h2> <p>浏览器中的缓存作用分为两种情况，一种是需要发送HTTP请求，一种是不需要发送。</p> <p>首先是检查强缓存，这个阶段不需要发送HTTP请求。</p> <p>如何来检查呢？通过相应的字段来进行，但是说起这个字段就有点门道了。</p> <p>在<code>HTTP/1.0</code>和<code>HTTP/1.1</code>当中，这个字段是不一样的。在早期，也就是HTTP/1.0时期，使用的是<strong>Expires</strong>，而HTTP/1.1使用的是<strong>Cache-Control</strong>。让我们首先来看看Expires。</p> <h3 id="expires"><a href="#expires" class="header-anchor">#</a> Expires</h3> <p>Expires即过期时间，存在于服务端返回的响应头中，告诉浏览器在这个过期时间之前可以直接从缓存里面获取数据，无需再次请求。比如下面这样:</p> <div class="language-js extra-class"><pre class="language-js"><code>Expires<span class="token operator">:</span> Wed<span class="token punctuation">,</span> <span class="token number">22</span> Nov <span class="token number">2019</span> <span class="token number">08</span><span class="token operator">:</span><span class="token number">41</span><span class="token operator">:</span><span class="token number">00</span> <span class="token constant">GMT</span>
</code></pre></div><p>表示资源在2019年11月22号8点41分过期，过期了就得向服务端发请求。</p> <p>这个方式看上去没什么问题，合情合理，但其实潜藏了一个坑，那就是<strong>服务器的时间和浏览器的时间可能并不一致</strong>，那服务器返回的这个过期时间可能就是不准确的。因此这种方式很快在后来的HTTP1.1版本中被抛弃了。</p> <h3 id="cache-control"><a href="#cache-control" class="header-anchor">#</a> Cache-Control</h3> <p>在HTTP1.1中，采用了一个非常关键的字段：Cache-Control。这个字段也是存在于</p> <p>它和Expires本质的不同在于它并没有采用具体的过期时间点这个方式，而是采用过期时长来控制缓存，对应的字段是<strong>max-age</strong>。比如这个例子:</p> <div class="language-js extra-class"><pre class="language-js"><code>Cache<span class="token operator">-</span>Control<span class="token operator">:</span>max<span class="token operator">-</span>age<span class="token operator">=</span><span class="token number">3600</span>
</code></pre></div><p>代表这个响应返回后在 3600 秒，也就是一个小时之内可以直接使用缓存。</p> <p>如果你觉得它只有max-age一个属性的话，那就大错特错了。</p> <p>它其实可以组合非常多的指令，完成更多场景的缓存判断, 将一些关键的属性列举如下: public: 客户端和代理服务器都可以缓存。因为一个请求可能要经过不同的代理服务器最后才到达目标服务器，那么结果就是不仅仅浏览器可以缓存数据，中间的任何代理节点都可以进行缓存。</p> <ul><li>private： 这种情况就是只有浏览器能缓存了，中间的代理服务器不能缓存。</li> <li>no-cache: 跳过当前的强缓存，发送HTTP请求，即直接进入协商缓存阶段。</li> <li>no-store：非常粗暴，不进行任何形式的缓存。</li> <li>s-maxage：这和max-age长得比较像，但是区别在于s-maxage是针对代理服务器的缓存时间。</li> <li>must-revalidate: 是缓存就会有过期的时候，加上这个字段一旦缓存过期，就必须回到源服务器验证。</li></ul> <p>值得注意的是，当Expires和Cache-Control同时存在的时候，<strong>Cache-Control</strong>会优先考虑。</p> <p>当然，还存在一种情况，当资源缓存时间超时了，也就是强缓存失效了，接下来怎么办？没错，这样就进入到第二级屏障——协商缓存了。</p> <h2 id="协商缓存"><a href="#协商缓存" class="header-anchor">#</a> 协商缓存</h2> <p>强缓存失效之后，浏览器在请求头中携带相应的缓存tag来向服务器发请求，由服务器根据这个tag，来决定是否使用缓存，这就是协商缓存。</p> <p>具体来说，这样的缓存tag分为两种: Last-Modified 和 ETag。这两者各有优劣，并不存在谁对谁有绝对的优势，跟上面强缓存的两个 tag 不一样。</p> <h3 id="last-modified"><a href="#last-modified" class="header-anchor">#</a> #Last-Modified</h3> <p>即最后修改时间。在浏览器第一次给服务器发送请求后，服务器会在响应头中加上这个字段。</p> <p>浏览器接收到后，如果再次请求，会在请求头中携带<code>If-Modified-Since</code>字段，这个字段的值也就是服务器传来的最后修改时间。</p> <p>服务器拿到请求头中的<code>If-Modified-Since</code>的字段后，其实会和这个服务器中该资源的最后修改时间对比:</p> <ul><li>如果请求头中的这个值小于最后修改时间，说明是时候更新了。返回新的资源，跟常规的HTTP请求响应的流程一样。</li> <li>否则返回304，告诉浏览器直接用缓存。</li></ul> <h3 id="etag"><a href="#etag" class="header-anchor">#</a> ETag</h3> <p>ETag 是服务器根据当前文件的内容，给文件生成的唯一标识，只要里面的内容有改动，这个值就会变。服务器通过响应头把这个值给浏览器。</p> <p>浏览器接收到ETag的值，会在下次请求时，将这个值作为<code>If-None-Match</code>这个字段的内容，并放到请求头中，然后发给服务器。</p> <p>服务器接收到<code>If-None-Match</code>后，会跟服务器上该资源的ETag进行比对:</p> <ul><li>如果两者不一样，说明要更新了。返回新的资源，跟常规的HTTP请求响应的流程一样。</li> <li>否则返回304，告诉浏览器直接用缓存。</li></ul> <h2 id="两者对比"><a href="#两者对比" class="header-anchor">#</a> 两者对比</h2> <ol><li>在精准度上，ETag优于Last-Modified。优于 ETag 是按照内容给资源上标识，因此能准确感知资源的变化。而 Last-Modified 就不一样了，它在一些特殊的情况并不能准确感知资源变化，主要有两种情况:
<ul><li>编辑了资源文件，但是文件内容并没有更改，这样也会造成缓存失效。</li> <li>Last-Modified 能够感知的单位时间是秒，如果文件在 1 秒内改变了多次，那么这时候的 Last-Modified 并没有体现出修改了。</li></ul></li> <li>在性能上，Last-Modified优于ETag，也很简单理解，Last-Modified仅仅只是记录一个时间点，而 Etag需要根据文件的具体内容生成哈希值。</li></ol> <p>另外，如果两种方式都支持的话，服务器会优先考虑ETag。</p> <h2 id="缓存位置"><a href="#缓存位置" class="header-anchor">#</a> 缓存位置</h2> <p>前面我们已经提到，当强缓存命中或者协商缓存中服务器返回304的时候，我们直接从缓存中获取资源。那这些资源究竟缓存在什么位置呢？</p> <p>浏览器中的缓存位置一共有四种，按优先级从高到低排列分别是</p> <ul><li>Service Worker</li> <li>Memory Cache</li> <li>Disk Cache</li> <li>Push Cache</li></ul> <h3 id="service-worker"><a href="#service-worker" class="header-anchor">#</a> Service Worker</h3> <p>Service Worker 借鉴了 Web Worker的 思路，即让 JS 运行在主线程之外，由于它脱离了浏览器的窗体，因此无法直接访问DOM。虽然如此，但它仍然能帮助我们完成很多有用的功能，比如离线缓存、消息推送和网络代理等功能。其中的离线缓存就是 <strong>Service Worker Cache</strong>。</p> <p>Service Worker 同时也是 PWA 的重要实现机制，关于它的细节和特性，我们将会在后面的 PWA 的分享中详细介绍</p> <h3 id="memory-cache-和-disk-cache"><a href="#memory-cache-和-disk-cache" class="header-anchor">#</a> Memory Cache 和 Disk Cache</h3> <p>Memory Cache指的是内存缓存，从效率上讲它是最快的。但是从存活时间来讲又是最短的，当渲染进程结束后，内存缓存也就不存在了。</p> <p>Disk Cache就是存储在磁盘中的缓存，从存取效率上讲是比内存缓存慢的，但是他的优势在于存储容量和存储时长。稍微有些计算机基础的应该很好理解，就不展开了。</p> <p>好，现在问题来了，既然两者各有优劣，那浏览器如何决定将资源放进内存还是硬盘呢？主要策略如下</p> <ul><li>比较大的JS、CSS文件会直接被丢进磁盘，反之丢进内存</li> <li>内存使用率比较高的时候，文件优先进入磁盘</li></ul> <h3 id="push-cache"><a href="#push-cache" class="header-anchor">#</a> Push Cache</h3> <p>即推送缓存，这是浏览器缓存的最后一道防线。它是 HTTP/2 中的内容，虽然现在应用的并不广泛，但随着 HTTP/2 的推广，它的应用越来越广泛。关于 Push Cache，有非常多的内容可以挖掘，不过这已经不是本文的重点</p> <h2 id="总结"><a href="#总结" class="header-anchor">#</a> 总结</h2> <p>对浏览器的缓存机制来做个简要的总结:</p> <p>首先通过 Cache-Control 验证强缓存是否可用</p> <ul><li>如果强缓存可用，直接使用</li> <li>否则进入协商缓存，即发送 HTTP 请求，服务器通过请求头中的If-Modified-Since或者If-None-Match这些条件请求字段检查资源是否更新
<ul><li>若资源更新，返回资源和200状态码</li> <li>否则，返回304，告诉浏览器直接从缓存获取资源</li></ul></li></ul></div> <footer class="page-edit"><!----> <!----></footer> <div class="page-nav"><p class="inner"><span class="prev">
      ←
      <a href="/http/012.html" class="prev">
        12 Http2
      </a></span> <span class="next"><a href="/extend/002.html">
        01.实现一个简单的Webpack
      </a>
      →
    </span></p></div> </main></div><div class="global-ui"></div></div>
    <script src="/assets/js/app.e358a08d.js" defer></script><script src="/assets/js/2.88fa18d1.js" defer></script><script src="/assets/js/35.fadf5d03.js" defer></script>
  </body>
</html>
